﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace PlutoStudio.Net.Messages
{
    [StructLayout(LayoutKind.Sequential)]
    public struct MessageHeader
    {
        public byte Flag;
        public byte Code;
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 MessageId;
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 Version;
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 StatusCode;
        public Int32 Length;
    }

    public class Message:IMessage
    {
        private MessageHeader Header;

        private byte[] Content;

        public Message()
        {

        }

        public Message(byte flag, byte code)
            : this(flag, code, 0, null)
        {

        }

        public Message(byte flag, byte code, UInt16 statusCode)
            : this(flag, code, statusCode, null)
        {
        }

        public Message(byte flag, byte code, byte[] data)
            : this(flag, code, 0, data)
        {

        }

        public Message(byte flag, byte code, UInt16 statusCode, byte[] data)
            : this(flag, code, statusCode, 0, data)
        {
        }

        public Message(byte flag, byte code, UInt16 statusCode, UInt16 version, byte[] data)
        {
            Header.MessageId = 0;
            Header.Flag = flag;
            Header.Code = code;
            Header.Version = version;
            Header.StatusCode = statusCode;
            Header.Length = data == null ? 0 : data.Length;
            Content = data;
        }

        internal Message(MessageHeader header, byte[] content)
        {
            Header = header;
            Content = content;
        }

        public byte[] ToBytes()
        {
            IntPtr ptr = IntPtr.Zero;
            var headSize = Marshal.SizeOf(typeof(MessageHeader));
            var size = headSize + Header.Length;
            var buffer = new byte[size];
            try
            {
                ptr = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(Header, ptr, false);
                Marshal.Copy(ptr, buffer, 0, headSize);
                if (Content != null)
                {
                    Buffer.BlockCopy(Content, 0, buffer, headSize, Header.Length);

                } return buffer;
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
        }

        public override string ToString()
        {
            var result = new { Header, Content = Content == null ? null : Encoding.UTF8.GetString(Content) };
            return result.Serialize();
        }

        public static Message FromBuffer(byte[] buffer)
        {
            return FromBuffer(buffer, buffer.Length);
        }


        public static Message FromBuffer(byte[] buffer, int count)
        {
            IntPtr ptr = IntPtr.Zero;
            var headSize = Marshal.SizeOf(typeof(MessageHeader));
            if (count < headSize)
            {
                return null;
            }
            try
            {
                ptr = Marshal.AllocHGlobal(headSize);
                Marshal.Copy(buffer, 0, ptr, headSize);
                var header = (MessageHeader)Marshal.PtrToStructure(ptr, typeof(MessageHeader));
                if (count - headSize < header.Length)
                {
                    return null;
                }
                var data = new byte[header.Length];
                Buffer.BlockCopy(buffer, headSize, data, 0, (int)header.Length);
                return new Message(header, data);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);

            }
        }


        public int Size { get { return Marshal.SizeOf(Header) + Header.Length; } }

        public int Length { get { return Header.Length; } set { Header.Length = value; } }

        public byte Flag { get { return Header.Flag; } set { Header.Flag = value; } }
        public byte Code { get { return Header.Code; } set { Header.Code = value; } }
        public UInt16 MessageId { get { return Header.MessageId; } set { Header.MessageId = value; } }
        public UInt16 Version { get { return Header.Version; } set { Header.Version = value; } }
        public UInt16 StatusCode { get { return Header.StatusCode; } set { Header.StatusCode = value; } }

        public byte[] Data { get { return Content; } set { Content = value; } }


        string IMessage.Code
        {
            get { return Flag.ToString(); }
        }

    }

    internal class MessageProvider : IMessageGenerater
    {
        public IMessage Read(byte[] buffer, int count)
        {
            return Message.FromBuffer(buffer, count);
        }


        public byte[] ToBytes(IMessage message)
        {
            var msg = message as Message;
            if (msg==null)
            {
                throw new InvalidOperationException("无法序列化当前消息对象。");
            }
            return msg.ToBytes();
        }
    }

}
